replicas-mcp 0.2.13 → 0.2.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/index.js +191 -75
- package/package.json +1 -1
- package/src/index.ts +2 -2
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# replicas-mcp
|
|
2
2
|
|
|
3
|
-
Stdio MCP server for [Replicas](https://
|
|
3
|
+
Stdio MCP server for [Replicas](https://replicas.dev) - cloud workspaces for AI coding agents.
|
|
4
4
|
|
|
5
|
-
For MCP clients that require a remote server URL instead of stdio, use the streamable HTTP endpoint at `https://api.
|
|
5
|
+
For MCP clients that require a remote server URL instead of stdio, use the streamable HTTP endpoint at `https://api.replicas.dev/v1/mcp`.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -20,7 +20,7 @@ For MCP clients that require a remote server URL instead of stdio, use the strea
|
|
|
20
20
|
}
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
Get your API key from [Settings > API Keys](https://
|
|
23
|
+
Get your API key from [Settings > API Keys](https://replicas.dev/dashboard/settings?tab=api-keys).
|
|
24
24
|
|
|
25
25
|
## Tools
|
|
26
26
|
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ var AGENT = {
|
|
|
12
12
|
CURSOR: "cursor",
|
|
13
13
|
DEEPSEEK: "deepseek",
|
|
14
14
|
FX: "fx",
|
|
15
|
+
KIMI: "kimi",
|
|
15
16
|
OPENCODE: "opencode",
|
|
16
17
|
PI: "pi",
|
|
17
18
|
RELAY: "relay"
|
|
@@ -32,6 +33,7 @@ var THINKING_LEVELS_BY_AGENT = {
|
|
|
32
33
|
cursor: STANDARD_THINKING_LEVELS,
|
|
33
34
|
deepseek: STANDARD_THINKING_LEVELS,
|
|
34
35
|
fx: STANDARD_THINKING_LEVELS,
|
|
36
|
+
kimi: STANDARD_THINKING_LEVELS,
|
|
35
37
|
opencode: STANDARD_THINKING_LEVELS,
|
|
36
38
|
pi: STANDARD_THINKING_LEVELS,
|
|
37
39
|
relay: [...STANDARD_THINKING_LEVELS, "ultracode"]
|
|
@@ -49,6 +51,8 @@ function getProviderDisplayName(provider, variant = "short") {
|
|
|
49
51
|
return variant === "long" ? "DeepSeek Harness" : "DeepSeek";
|
|
50
52
|
case "fx":
|
|
51
53
|
return "fx";
|
|
54
|
+
case "kimi":
|
|
55
|
+
return variant === "long" ? "Kimi Code" : "Kimi";
|
|
52
56
|
case "opencode":
|
|
53
57
|
return "Opencode";
|
|
54
58
|
case "pi":
|
|
@@ -125,6 +129,9 @@ var AGENT_CREDENTIAL_METHODS_IN_ORDER = {
|
|
|
125
129
|
[AGENT.FX]: [
|
|
126
130
|
{ method: CREDENTIAL_METHOD.AI_GATEWAY, credentialType: CREDENTIAL_TYPE.AI_GATEWAY_API_KEY }
|
|
127
131
|
],
|
|
132
|
+
[AGENT.KIMI]: [
|
|
133
|
+
{ method: CREDENTIAL_METHOD.AI_GATEWAY, credentialType: CREDENTIAL_TYPE.AI_GATEWAY_API_KEY }
|
|
134
|
+
],
|
|
128
135
|
[AGENT.OPENCODE]: [
|
|
129
136
|
{ method: CREDENTIAL_METHOD.OPENCODE_GO, credentialType: CREDENTIAL_TYPE.OPENCODE_GO_API_KEY },
|
|
130
137
|
{ method: CREDENTIAL_METHOD.ASTER, credentialType: CREDENTIAL_TYPE.ASTER_API_KEY },
|
|
@@ -174,6 +181,7 @@ var WEB_APP_PATHS = {
|
|
|
174
181
|
automations: `${HOME_PATH}/automations`,
|
|
175
182
|
environment: `${HOME_PATH}/environment`,
|
|
176
183
|
analytics: `${HOME_PATH}/analytics`,
|
|
184
|
+
memory: `${HOME_PATH}/memory`,
|
|
177
185
|
dashboard: DASHBOARD_PATH,
|
|
178
186
|
agents: `${DASHBOARD_PATH}/agents`,
|
|
179
187
|
integrations: `${DASHBOARD_PATH}/integrations`,
|
|
@@ -185,6 +193,7 @@ var WEB_APP_PATHS = {
|
|
|
185
193
|
"audit-logs": `${DASHBOARD_PATH}/audit-logs`,
|
|
186
194
|
accountAgents: `${DASHBOARD_PATH}/account/agents`,
|
|
187
195
|
accountProfile: `${DASHBOARD_PATH}/account/profile`,
|
|
196
|
+
accountConnections: `${DASHBOARD_PATH}/account/connections`,
|
|
188
197
|
accountIntegrations: `${DASHBOARD_PATH}/account/integrations`,
|
|
189
198
|
accountApiKeys: `${DASHBOARD_PATH}/account/api-keys`,
|
|
190
199
|
accountPreferences: `${DASHBOARD_PATH}/account/preferences`,
|
|
@@ -257,10 +266,12 @@ replicas computer record stop
|
|
|
257
266
|
replicas media upload /tmp/demo.mp4 --name "Demo recording"
|
|
258
267
|
\`\`\`
|
|
259
268
|
|
|
269
|
+
For any external destination, continue with the required delivery flow in \`MEDIA.md\`.
|
|
270
|
+
|
|
260
271
|
## Command reference
|
|
261
272
|
|
|
262
273
|
### \`replicas computer info\`
|
|
263
|
-
Prints the live desktop viewer URL (\`https://6080-<hash>.
|
|
274
|
+
Prints the live desktop viewer URL (\`https://6080-<hash>.replicas.dev/\`). The engine auto-registers this authenticated preview on startup; \`info\` just looks it up. Use it when you want to share the live stream URL with the user out-of-band (e.g. in a Slack reply) - the dashboard \`Desktop\` tab already points at the same URL automatically.
|
|
264
275
|
|
|
265
276
|
If invoked very early in the workspace lifecycle, \`info\` will poll briefly while the engine finishes registering the preview, then error if it's still not available.
|
|
266
277
|
|
|
@@ -391,7 +402,7 @@ replicas computer record start /tmp/walkthrough.mp4
|
|
|
391
402
|
replicas computer record stop
|
|
392
403
|
replicas media upload /tmp/walkthrough.mp4 --name "Walkthrough recording"
|
|
393
404
|
\`\`\`
|
|
394
|
-
Then embed the printed \`\` line in your chat reply
|
|
405
|
+
Then embed the printed \`\` line in your chat reply and follow \`MEDIA.md\` for any external destination.
|
|
395
406
|
|
|
396
407
|
## Failure modes
|
|
397
408
|
|
|
@@ -591,16 +602,9 @@ gh pr review 123 --approve
|
|
|
591
602
|
gh pr review 123 --request-changes --body "Changes needed"
|
|
592
603
|
\`\`\`
|
|
593
604
|
|
|
594
|
-
##
|
|
605
|
+
## Media in GitHub workflows
|
|
595
606
|
|
|
596
|
-
GitHub
|
|
597
|
-
- Do NOT use placeholder image URLs
|
|
598
|
-
- Do NOT commit screenshots as files to the repository
|
|
599
|
-
- Run \`replicas media upload <image> --name "<display name>" --share\` and put the printed \`Forge embed\` Markdown in the PR body or comment
|
|
600
|
-
- For a self-contained HTML report, run \`replicas media upload <page.html> --name "<display name>" --access organization\` and put the printed \`Forge link\` Markdown in the PR body or comment. Organization members who are already signed in open it directly; others sign in and return to the page
|
|
601
|
-
- For video, embed a shared poster image linked to the recording's **View in Replicas** URL; GitHub does not reliably render externally hosted video inline
|
|
602
|
-
- Treat the public URL as an opt-in bearer capability; revoke it with \`replicas media revoke <media-id>\` only when the user asks or the media should stop rendering \u2014 revoking breaks embeds already posted on the PR
|
|
603
|
-
- If you were triggered from Slack, also upload the image to the Slack thread so the user can see it directly
|
|
607
|
+
Media delivery is owned by \`references/MEDIA.md\`. Read its GitHub section before adding screenshots, recordings, diagrams, or HTML reports to a pull request, issue, review comment, or commit; it covers the Replicas upload, GitHub's inline fallback, and the required **View in Replicas** link.
|
|
604
608
|
`;
|
|
605
609
|
var GITHUB_ABILITY = {
|
|
606
610
|
label: "GitHub",
|
|
@@ -662,7 +666,7 @@ await replicas.gitlab.request('/projects/group%2Frepo/pipelines', {
|
|
|
662
666
|
|
|
663
667
|
## Descriptions and media
|
|
664
668
|
|
|
665
|
-
API updates replace the entire merge request description, so always preserve the required Replicas footer and co-author trailers.
|
|
669
|
+
API updates replace the entire merge request description, so always preserve the required Replicas footer and co-author trailers. Media delivery is owned by \`references/MEDIA.md\`; read its GitLab section before adding media so the original bytes are uploaded to Replicas and GitLab and the dashboard link remains secondary.
|
|
666
670
|
`;
|
|
667
671
|
var GITLAB_ABILITY = {
|
|
668
672
|
label: "GitLab",
|
|
@@ -727,6 +731,8 @@ await replicas.linear.graphql({
|
|
|
727
731
|
\`\`\`
|
|
728
732
|
|
|
729
733
|
Search with \`containsIgnoreCase: $query\` filters, for example on both \`title\` and \`description\`. Batch independent queries with \`Promise.all\` to save round trips.
|
|
734
|
+
|
|
735
|
+
For screenshots, recordings, diagrams, or other media, read \`references/MEDIA.md\` before adding it to an issue or comment. It is the single source of truth for uploading the Replicas copy and the raw file to Linear; a dashboard link alone is not an inline attachment.
|
|
730
736
|
`;
|
|
731
737
|
var LINEAR_ABILITY = {
|
|
732
738
|
label: "Linear",
|
|
@@ -738,18 +744,19 @@ var LINEAR_ABILITY = {
|
|
|
738
744
|
|
|
739
745
|
// ../shared/src/default-skills/replicas-agent/abilities/media.ts
|
|
740
746
|
var SECTION6 = `### Media
|
|
741
|
-
|
|
747
|
+
Deliver screenshots, screen recordings, generated diagrams, audio clips, and self-contained HTML pages to the Replicas dashboard and, when applicable, inline in the destination workflow.
|
|
742
748
|
|
|
743
749
|
**Reference:** \`references/MEDIA.md\`
|
|
744
750
|
|
|
745
751
|
Use this when:
|
|
752
|
+
- You are about to run \`replicas media upload\`; this reference owns the complete Replicas-first, destination-second workflow
|
|
746
753
|
- You produce a screenshot, recording, generated image, or audio clip the user should see
|
|
747
754
|
- You produce a self-contained HTML report that should be linked from a pull request
|
|
748
755
|
- You record video output (browser automation, screen capture) \u2014 including the recommended aspect ratio and FPS
|
|
749
|
-
- You need to embed media in a Slack/Linear/GitHub message AND keep a referenceable copy in the Replicas dashboard`;
|
|
756
|
+
- You need to embed media in a Slack/Linear/GitHub/GitLab message AND keep a referenceable copy in the Replicas dashboard`;
|
|
750
757
|
var REFERENCE6 = `# Media (Screenshots, Recordings, Audio, HTML)
|
|
751
758
|
|
|
752
|
-
This
|
|
759
|
+
This is the single source of truth for delivering screenshots, screen recordings, generated diagrams, audio clips, and self-contained HTML pages through Replicas and external surfaces (Slack, Linear, GitHub, GitLab).
|
|
753
760
|
|
|
754
761
|
## Prerequisites
|
|
755
762
|
|
|
@@ -759,12 +766,32 @@ The \`replicas\` CLI is pre-installed and authenticated in your workspace. No ad
|
|
|
759
766
|
|
|
760
767
|
Upload to Replicas in these cases \u2014 and **only** these cases:
|
|
761
768
|
|
|
762
|
-
1. **Media you produce.** Any screenshot, screen recording, generated diagram,
|
|
769
|
+
1. **Media you produce.** Any screenshot, screen recording, generated diagram, audio clip, or HTML page you create that the user might want to see. Upload before doing anything else with the file (analyzing, deleting, sending elsewhere). This applies even when you're also sending the file to Slack, Linear, GitHub, or GitLab.
|
|
763
770
|
2. **Files the user explicitly asks you to upload.** If the user sends or points at a file (image, video, audio, HTML) and asks you to upload it, run \`replicas media upload\`. Otherwise leave it alone \u2014 files in the workspace the user did not ask about should not be auto-uploaded as media.
|
|
764
|
-
3. **Anything you plan to share externally (Slack, Linear, GitHub, etc.).** Upload to Replicas *in addition to* the platform's
|
|
771
|
+
3. **Anything you plan to share externally (Slack, Linear, GitHub, GitLab, etc.).** Upload to Replicas *in addition to* the platform's destination upload. Never as a replacement.
|
|
765
772
|
|
|
766
773
|
If none of these apply, don't upload.
|
|
767
774
|
|
|
775
|
+
## Required delivery contract
|
|
776
|
+
|
|
777
|
+
\`replicas media upload\` is always the first delivery action. When the current task has an external destination, every other delivery step follows only after that command succeeds. For every file:
|
|
778
|
+
|
|
779
|
+
1. First, run \`replicas media upload\` with the original file. Do not begin any destination upload until the command succeeds. This creates the canonical dashboard copy and prints the authenticated chat embed plus a per-file dashboard link.
|
|
780
|
+
2. Include the printed embed line in the Replicas chat reply, followed by the matching **View in Replicas** link. HTML uses its printed page link instead of an image embed.
|
|
781
|
+
3. Identify every destination in the task and attempt to upload the same original bytes there. Use the destination's inline representation from the table below; do not replace the file with a Replicas link.
|
|
782
|
+
4. Put the **View in Replicas** dashboard link next to the destination embed as a secondary link.
|
|
783
|
+
5. Check each destination response. If a native upload is unavailable or fails, say so explicitly and keep the dashboard link; never claim that a link is an inline upload.
|
|
784
|
+
|
|
785
|
+
| Destination | Required file delivery | Inline result |
|
|
786
|
+
|---|---|---|
|
|
787
|
+
| Replicas dashboard/chat | \`replicas media upload\` | Printed \`\` line in chat |
|
|
788
|
+
| Slack | \`files.getUploadURLExternal\` \u2192 raw-byte POST \u2192 \`files.completeUploadExternal\` | Uploaded file in the channel or thread |
|
|
789
|
+
| Linear | \`fileUpload\` mutation \u2192 presigned upload \u2192 issue/comment mutation | Returned \`assetUrl\` in Markdown |
|
|
790
|
+
| GitLab | Project \`/uploads\` endpoint with the original bytes | Returned GitLab Markdown |
|
|
791
|
+
| GitHub | No supported attachment API; use a Replicas public forge share for the inline image or video poster | Printed Forge embed, linked to the recording when needed |
|
|
792
|
+
|
|
793
|
+
The dashboard link is the subtitle and jump-back path. It is never a substitute for the destination upload.
|
|
794
|
+
|
|
768
795
|
## Uploading to Replicas
|
|
769
796
|
|
|
770
797
|
\`\`\`bash
|
|
@@ -792,7 +819,7 @@ Names are normalized to hyphen-case (spaces become hyphens, e.g. \`Login page\`
|
|
|
792
819
|
For each media file, the CLI prints two lines:
|
|
793
820
|
|
|
794
821
|
1. \`\` \u2014 the chat embed (renders inline in Replicas chat **only** \u2014 see below).
|
|
795
|
-
2. \`View in Replicas: <deep-link>\` \u2014 a per-file dashboard URL of the form \`https://
|
|
822
|
+
2. \`View in Replicas: <deep-link>\` \u2014 a per-file dashboard URL of the form \`https://replicas.dev/workspaces/<workspace-id>?media=<media-id>\` that opens directly to that specific file.
|
|
796
823
|
|
|
797
824
|
Match each "View in Replicas" line to the embed line directly above it \u2014 that's the deep link for that file.
|
|
798
825
|
|
|
@@ -818,19 +845,20 @@ replicas media upload screenshot.png --name "Login page" --access public
|
|
|
818
845
|
|
|
819
846
|
| URL | What it is | Where it's allowed |
|
|
820
847
|
|---|---|---|
|
|
821
|
-
| \`https://api.
|
|
822
|
-
| \`https://
|
|
823
|
-
| \`https://api.
|
|
824
|
-
| \`https://
|
|
848
|
+
| \`https://api.replicas.dev/v1/media/<id>\` (inside \`\`) | Authenticated API redirect to a presigned blob \u2014 requires the Replicas chat session | **Only** in the Replicas chat reply. Nowhere else. |
|
|
849
|
+
| \`https://replicas.dev/organizations/<organization-id>/media/<media-id>\` | Organization-only page printed by \`--access organization\` | Pull requests and other places where every viewer belongs to the Replicas organization. |
|
|
850
|
+
| \`https://api.replicas.dev/v1/media/public/<id>/<token>\` | Opt-in public bearer URL printed by \`--access public\` or \`--share\` | GitHub embeds for image, video, or audio media. |
|
|
851
|
+
| \`https://replicas.dev/workspaces/<workspace-id>?media=<media-id>\` | Authenticated dashboard deep link that opens the media file in the workspace | Everywhere external: Slack, GitHub, GitLab, Linear, PR bodies, comments, emails. |
|
|
825
852
|
|
|
826
853
|
### NEVER link the API redirect URL outside the Replicas chat reply
|
|
827
854
|
|
|
828
|
-
The \`api.
|
|
855
|
+
The \`api.replicas.dev/v1/media/<id>\` URL is **not public**. It's an authenticated redirect that only resolves to a presigned download when the Replicas chat session token is present. Anywhere else (Slack, Linear, GitHub, a browser tab the user opens directly, a customer copy-pasting from chat) it returns \`{"error":"Missing authorization token"}\` and renders as a broken image.
|
|
829
856
|
|
|
830
857
|
This rule has no exceptions:
|
|
831
858
|
- \u274C Never paste it in a Slack message \u2014 upload the actual bytes via the 3-step \`files.getUploadURLExternal\` \u2192 POST bytes \u2192 \`files.completeUploadExternal\` flow instead (see below).
|
|
832
859
|
- \u274C Never paste it in a GitHub PR description, issue body, review comment, or commit message.
|
|
833
860
|
- \u274C Never paste it in a Linear issue body or comment \u2014 upload via Linear's \`fileUpload\` mutation instead (see below).
|
|
861
|
+
- \u274C Never paste it in a GitLab merge request or issue; upload the original bytes through GitLab's project upload endpoint instead (see below).
|
|
834
862
|
- \u274C Never link to it from any external doc, email, or external chat.
|
|
835
863
|
- \u274C Never include it as a "backup link" in case the dashboard link breaks \u2014 it will always break externally.
|
|
836
864
|
|
|
@@ -839,7 +867,7 @@ This rule has no exceptions:
|
|
|
839
867
|
For an organization-only HTML page, use the \`Forge link\` printed by \`--access organization\`. For other authenticated media references, use the dashboard form:
|
|
840
868
|
|
|
841
869
|
\`\`\`
|
|
842
|
-
https://
|
|
870
|
+
https://replicas.dev/workspaces/<workspace-id>?media=<media-id>
|
|
843
871
|
\`\`\`
|
|
844
872
|
|
|
845
873
|
The \`media=<media-id>\` query param opens that specific file in the workspace.
|
|
@@ -854,20 +882,20 @@ Include each markdown embed line **verbatim** where you want that file to render
|
|
|
854
882
|
|
|
855
883
|
After (or alongside) the embeds, include a \`[View in Replicas](<deep-link>)\` hyperlink for each file using the per-file dashboard URL from the CLI. This lets the user open the specific item in the workspace.
|
|
856
884
|
|
|
857
|
-
## On external platforms
|
|
885
|
+
## On external platforms: embed the file AND link the dashboard
|
|
858
886
|
|
|
859
|
-
|
|
887
|
+
After \`replicas media upload\` succeeds, external delivery has two required follow-up steps for every file:
|
|
860
888
|
|
|
861
|
-
1. **Upload the raw bytes
|
|
889
|
+
1. **Upload the raw bytes through that platform's supported inline path** so the recipient sees the media embedded in the message itself.
|
|
862
890
|
2. **Link the Replicas dashboard deep link** (\`?media=<id>\` form) so the user can jump straight to the file in the workspace.
|
|
863
891
|
|
|
864
|
-
A link alone is not enough.
|
|
892
|
+
A link alone is not enough. An upload without the dashboard link is not enough. Do both.
|
|
865
893
|
|
|
866
|
-
GitHub has no
|
|
894
|
+
Slack, Linear, and GitLab receive the original bytes through their native upload APIs. GitHub has no supported attachment API, so use an explicit Replicas public share for screenshots and recording previews.
|
|
867
895
|
|
|
868
896
|
### Slack \u2014 embed natively via the 3-step external file upload flow
|
|
869
897
|
|
|
870
|
-
A Slack message that just contains a \`
|
|
898
|
+
A Slack message that just contains a \`replicas.dev\` link is not acceptable when the message is supposed to show an image. The recipient should see the image inline in the thread.
|
|
871
899
|
|
|
872
900
|
Slack deprecated the single-shot \`files.upload\` endpoint. The raw HTTP replacement is a 3-step flow: reserve an upload URL with \`files.getUploadURLExternal\`, POST the bytes to it, then share into the channel/thread with \`files.completeUploadExternal\`. (\`files.uploadV2\` is **only** an SDK convenience wrapper \u2014 it is NOT a real Web API HTTP endpoint and \`POST https://slack.com/api/files.uploadV2\` will return \`unknown_method\`.)
|
|
873
901
|
|
|
@@ -899,7 +927,7 @@ curl -s -X POST "https://slack.com/api/files.completeUploadExternal" \\
|
|
|
899
927
|
\\"files\\": [{\\"id\\": \\"$FILE_ID\\", \\"title\\": \\"Screenshot\\"}],
|
|
900
928
|
\\"channel_id\\": \\"CHANNEL_ID\\",
|
|
901
929
|
\\"thread_ts\\": \\"THREAD_TS\\",
|
|
902
|
-
\\"initial_comment\\": \\"<https://
|
|
930
|
+
\\"initial_comment\\": \\"<https://replicas.dev/workspaces/<workspace-id>?media=<media-id>|View in Replicas>\\"
|
|
903
931
|
}"
|
|
904
932
|
\`\`\`
|
|
905
933
|
|
|
@@ -910,7 +938,7 @@ Notes:
|
|
|
910
938
|
- For multiple files in the same message, run steps 1\u20132 per file and pass all \`{id, title}\` objects in the \`files\` array of a single \`files.completeUploadExternal\` call \u2014 they share into the thread together.
|
|
911
939
|
- If you also need to post a separate text reply in the thread, send it with \`chat.postMessage\` after the upload \u2014 but the upload itself must carry the image.
|
|
912
940
|
|
|
913
|
-
Never paste the \`api.
|
|
941
|
+
Never paste the \`api.replicas.dev/v1/media/<id>\` URL into a Slack message body or \`initial_comment\` \u2014 it will render as a broken image attempt.
|
|
914
942
|
|
|
915
943
|
### Linear \u2014 embed natively via the GraphQL \`fileUpload\` mutation
|
|
916
944
|
|
|
@@ -959,7 +987,7 @@ curl -s -X PUT "$UPLOAD_URL" \\
|
|
|
959
987
|
--data-binary @"$FILE"
|
|
960
988
|
|
|
961
989
|
# 3. Reference assetUrl inline in the comment body, alongside the dashboard link.
|
|
962
|
-
BODY=$(printf '\\n\\n[View in Replicas](https://
|
|
990
|
+
BODY=$(printf '\\n\\n[View in Replicas](https://replicas.dev/workspaces/%s?media=%s)' \\
|
|
963
991
|
"$ASSET_URL" "$REPLICAS_WORKSPACE_ID" "$MEDIA_ID")
|
|
964
992
|
|
|
965
993
|
jq -n \\
|
|
@@ -973,7 +1001,7 @@ jq -n \\
|
|
|
973
1001
|
|
|
974
1002
|
The same flow works for issue bodies (use \`issueCreate\` / \`issueUpdate\` with the same markdown body).
|
|
975
1003
|
|
|
976
|
-
Never paste the \`api.
|
|
1004
|
+
Never paste the \`api.replicas.dev/v1/media/<id>\` URL into a Linear body or comment \u2014 it will render as a broken image to anyone outside your Replicas chat.
|
|
977
1005
|
|
|
978
1006
|
### GitHub: inline screenshots and recording previews
|
|
979
1007
|
|
|
@@ -994,7 +1022,7 @@ GitHub does not reliably render arbitrary external videos as inline players. For
|
|
|
994
1022
|
[](<recording-dashboard-url>)
|
|
995
1023
|
\`\`\`
|
|
996
1024
|
|
|
997
|
-
Public forge shares are bearer capabilities. For private repositories, create one only when the user asked for inline PR media. Revoke with \`replicas media revoke <media-id>\` **only** when the user asks or the media should intentionally stop rendering
|
|
1025
|
+
Public forge shares are unauthenticated bearer capabilities. For private repositories, create one only when the user explicitly asked for inline PR media. Without that consent, keep the media private in Replicas and report that GitHub has no supported attachment API; do not publish a bearer share or pretend the dashboard link is an embed. Revoke with \`replicas media revoke <media-id>\` **only** when the user asks or the media should intentionally stop rendering; never as routine post-task cleanup, since revoking breaks every embed already posted on the PR.
|
|
998
1026
|
|
|
999
1027
|
For a self-contained HTML report, upload it with \`--access organization\` and put the printed \`Forge link\` Markdown in the PR body or comment. GitHub cannot render the page inline, so use the normal link rather than image syntax. HTML pages cannot be shared publicly.
|
|
1000
1028
|
|
|
@@ -1002,7 +1030,7 @@ Do **not** commit screenshots to the repo, use placeholder URLs, expose the auth
|
|
|
1002
1030
|
|
|
1003
1031
|
### GitLab: native inline screenshots and videos
|
|
1004
1032
|
|
|
1005
|
-
GitLab supports native project Markdown uploads.
|
|
1033
|
+
GitLab supports native project Markdown uploads. For a GitLab destination, always upload the media to Replicas first, then POST the same original file to the repository's \`/projects/:id/uploads\` API using the workspace GitLab credential. Insert the returned \`markdown\` value into the merge request body or comment and include the Replicas dashboard link. Image uploads render inline; MP4, MOV, and WebM uploads render as inline video players.
|
|
1006
1034
|
|
|
1007
1035
|
Do not use a Replicas public forge share for GitLab when the native project upload succeeds; native uploads preserve project access controls.
|
|
1008
1036
|
|
|
@@ -1051,7 +1079,7 @@ replicas media upload chart-a.svg chart-b.svg --name "Latency chart" --name "Err
|
|
|
1051
1079
|
`;
|
|
1052
1080
|
var MEDIA_ABILITY = {
|
|
1053
1081
|
label: "Media",
|
|
1054
|
-
description: "
|
|
1082
|
+
description: "Deliver media to Replicas and destination workflows with inline uploads.",
|
|
1055
1083
|
bullet: "- Producing or showing the user any media \u2014 screenshots, screen recordings, generated images or diagrams, audio clips, or HTML recap pages \u2014 including in your Replicas chat reply, PR descriptions/comments, and other external platforms",
|
|
1056
1084
|
section: SECTION6,
|
|
1057
1085
|
referenceFile: { name: "MEDIA.md", content: REFERENCE6 }
|
|
@@ -1154,19 +1182,16 @@ Discover and execute installed marketplace tools through the workspace-scoped Ty
|
|
|
1154
1182
|
**Reference:** \`references/PLUGINS.md\`
|
|
1155
1183
|
|
|
1156
1184
|
Use this when:
|
|
1157
|
-
- The user asks
|
|
1158
|
-
- The user asks to
|
|
1159
|
-
- The user asks to read or change Google Ads, Docs, Drive, Search Console, or Sheets data
|
|
1160
|
-
- The user asks for ClickHouse analytics or PostHog product data
|
|
1185
|
+
- The user asks to work with an installed productivity, data, engineering, or business plugin
|
|
1186
|
+
- The user asks to discover which external services are connected
|
|
1161
1187
|
- The user asks to create or control an E2B sandbox
|
|
1162
1188
|
- The user asks to invoke a connected Modal function
|
|
1163
1189
|
- The user asks to query or update a connected turbopuffer namespace
|
|
1164
1190
|
- The user asks for PlanetScale organizations, databases, branches, or Insights data
|
|
1165
|
-
- The user asks to analyze text with Pangram
|
|
1166
|
-
- The user asks for Stripe customers, payments, invoices, subscriptions, or balances`;
|
|
1191
|
+
- The user asks to analyze text with Pangram`;
|
|
1167
1192
|
var REFERENCE8 = `# Plugins
|
|
1168
1193
|
|
|
1169
|
-
Plugins are TypeScript libraries, not MCP servers. Import \`@replicas/sdk\`; Replicas authenticates the workspace and selects
|
|
1194
|
+
Plugins are TypeScript libraries, not MCP servers. Import \`@replicas/sdk\`; Replicas authenticates the workspace and selects the owner\u2019s personal connection first, then the most specific environment the workspace inherits from, then Global. Provider credentials, sessions, and project keys are never exposed.
|
|
1170
1195
|
|
|
1171
1196
|
Plugin tools may read or write provider data according to the connected account's permissions. Only execute write or destructive tools when they match the user's request.
|
|
1172
1197
|
|
|
@@ -1179,7 +1204,7 @@ const plugins = await replicas.plugins.list();
|
|
|
1179
1204
|
const installed = plugins.filter((plugin) => plugin.connected);
|
|
1180
1205
|
\`\`\`
|
|
1181
1206
|
|
|
1182
|
-
If a plugin is not connected, ask the user to
|
|
1207
|
+
If a plugin is not connected, ask the user to connect it under personal Connections or the relevant environment's Plugins tab. A connection on Global reaches every workspace unless a more specific environment overrides it.
|
|
1183
1208
|
|
|
1184
1209
|
## Search, inspect, execute
|
|
1185
1210
|
|
|
@@ -1336,7 +1361,7 @@ In agent mode the CLI hides commands that don't make sense for in-workspace agen
|
|
|
1336
1361
|
| \`replicas automation ...\` | Manage automations (cron + GitHub/GitLab event triggers) |
|
|
1337
1362
|
| \`replicas preview ...\` | Register / list preview URLs (covered in \`PREVIEWS.md\`) |
|
|
1338
1363
|
| \`replicas service ...\` | Run long-lived services detached so they survive workspace sleep/wake (see below) |
|
|
1339
|
-
| \`replicas media ...\` | Upload
|
|
1364
|
+
| \`replicas media ...\` | Upload media to the dashboard and follow \`MEDIA.md\` for destination embedding |
|
|
1340
1365
|
| \`replicas slack thread ...\` | Attach or switch Slack thread routing (covered in \`SLACK.md\`) |
|
|
1341
1366
|
|
|
1342
1367
|
\`replicas <command> --help\` is always the source of truth for flags.
|
|
@@ -1630,6 +1655,8 @@ await replicas.slack.call('chat.postMessage', {
|
|
|
1630
1655
|
|
|
1631
1656
|
Only the announcing message gets the button \u2014 follow-ups about a pull request already linked in the thread keep the link in the text and drop the \`blocks\`. Use "View MR" for GitLab. With several links, give each button a unique \`action_id\` and name its repository in the label ("View PR \xB7 web").
|
|
1632
1657
|
|
|
1658
|
+
For screenshots, recordings, diagrams, or other media, read \`references/MEDIA.md\` before posting. It is the single source of truth for uploading the Replicas copy and the raw file to Slack; a dashboard link in \`text\` is not an inline attachment.
|
|
1659
|
+
|
|
1633
1660
|
Attach every new top-level conversation so future replies route to this workspace. For a reply in another thread, attach its root \`thread_ts\`, not the reply timestamp. Do not attach the originating thread again.
|
|
1634
1661
|
|
|
1635
1662
|
## Search and batch
|
|
@@ -1751,11 +1778,14 @@ var GPT_5_6_TERRA_MODEL = "gpt-5.6-terra";
|
|
|
1751
1778
|
var GPT_5_6_LUNA_MODEL = "gpt-5.6-luna";
|
|
1752
1779
|
var DEFAULT_CODEX_MODEL = GPT_5_6_SOL_MODEL;
|
|
1753
1780
|
var DEFAULT_OPENCODE_MODEL = "z-ai/glm-5.2";
|
|
1781
|
+
var OX_ALPHA_MODEL = "stealth/ox-alpha";
|
|
1754
1782
|
var DEEPSEEK_V4_PRO_MODEL = "deepseek/deepseek-v4-pro-0813";
|
|
1755
1783
|
var DEEPSEEK_V4_FLASH_MODEL = "deepseek/deepseek-v4-flash-0731";
|
|
1756
1784
|
var DEFAULT_FX_MODEL = "zai/glm-5.2";
|
|
1785
|
+
var DEFAULT_KIMI_MODEL = "moonshotai/kimi-k2.6";
|
|
1757
1786
|
var OPENROUTER_MODELS = [
|
|
1758
1787
|
DEFAULT_OPENCODE_MODEL,
|
|
1788
|
+
OX_ALPHA_MODEL,
|
|
1759
1789
|
DEEPSEEK_V4_PRO_MODEL,
|
|
1760
1790
|
DEEPSEEK_V4_FLASH_MODEL,
|
|
1761
1791
|
"minimax/minimax-m3",
|
|
@@ -1825,6 +1855,7 @@ var AGENT_MODELS = {
|
|
|
1825
1855
|
],
|
|
1826
1856
|
deepseek: prioritizeDeepseekModels(OPENROUTER_MODELS),
|
|
1827
1857
|
fx: [DEFAULT_FX_MODEL],
|
|
1858
|
+
kimi: [DEFAULT_KIMI_MODEL],
|
|
1828
1859
|
opencode: [...OPENROUTER_MODELS, ...ASTER_MODELS],
|
|
1829
1860
|
pi: [...OPENROUTER_MODELS, ...ASTER_MODELS],
|
|
1830
1861
|
relay: [CLAUDE_FABLE_5_MODEL, CLAUDE_OPUS_5_MODEL, CLAUDE_OPUS_4_8_MODEL, CLAUDE_SONNET_5_MODEL]
|
|
@@ -1844,6 +1875,7 @@ var MODEL_LABELS = {
|
|
|
1844
1875
|
[GPT_5_6_LUNA_MODEL]: "GPT-5.6 Luna",
|
|
1845
1876
|
"gpt-5.5": "GPT-5.5",
|
|
1846
1877
|
[DEFAULT_OPENCODE_MODEL]: "GLM 5.2 via OpenRouter",
|
|
1878
|
+
[OX_ALPHA_MODEL]: "Ox Alpha via OpenRouter",
|
|
1847
1879
|
[DEEPSEEK_V4_PRO_MODEL]: "DeepSeek V4 Pro via OpenRouter",
|
|
1848
1880
|
[DEEPSEEK_V4_FLASH_MODEL]: "DeepSeek V4 Flash via OpenRouter",
|
|
1849
1881
|
"minimax/minimax-m3": "MiniMax M3 via OpenRouter",
|
|
@@ -2863,71 +2895,146 @@ function parseDeepseekEvents(events) {
|
|
|
2863
2895
|
return messages;
|
|
2864
2896
|
}
|
|
2865
2897
|
|
|
2866
|
-
// ../shared/src/display-message/parsers/
|
|
2867
|
-
|
|
2898
|
+
// ../shared/src/display-message/parsers/acp-parser.ts
|
|
2899
|
+
var FX_CONTEXT_DIAGNOSTIC = /^\[context\] skill catalog omitted .*?(?:\r?\n|$)/;
|
|
2900
|
+
function getAcpTextChunk(update, kind) {
|
|
2868
2901
|
if (!isRecord(update) || update.sessionUpdate !== kind || !isRecord(update.content) || update.content.type !== "text") return null;
|
|
2869
|
-
return typeof update.content.text === "string" && update.content.text ? update.content.text : null;
|
|
2902
|
+
return typeof update.content.text === "string" && update.content.text.length > 0 ? update.content.text : null;
|
|
2870
2903
|
}
|
|
2871
|
-
function
|
|
2904
|
+
function isProviderEvent(event, provider, suffix) {
|
|
2905
|
+
return event.type === `acp-${suffix}` && (event.payload.provider === void 0 || event.payload.provider === provider) || event.type === `${provider}-${suffix}`;
|
|
2906
|
+
}
|
|
2907
|
+
function parseAcpEvents(events, provider) {
|
|
2872
2908
|
const messages = [];
|
|
2909
|
+
const activeChunks = {};
|
|
2910
|
+
const resolvedInputs = /* @__PURE__ */ new Map();
|
|
2911
|
+
let chunkSequence = 0;
|
|
2873
2912
|
for (const event of events) {
|
|
2874
|
-
const
|
|
2875
|
-
if (
|
|
2876
|
-
|
|
2913
|
+
const userContent = getUserMessage(event);
|
|
2914
|
+
if (userContent !== null) {
|
|
2915
|
+
activeChunks.agent_message_chunk = void 0;
|
|
2916
|
+
activeChunks.agent_thought_chunk = void 0;
|
|
2917
|
+
if (userContent.trim()) {
|
|
2877
2918
|
messages.push({
|
|
2878
|
-
id: getUserMessageId(event) ??
|
|
2919
|
+
id: getUserMessageId(event) ?? `${provider}-user-${event.timestamp}`,
|
|
2879
2920
|
type: "user",
|
|
2880
|
-
content,
|
|
2921
|
+
content: userContent,
|
|
2881
2922
|
timestamp: event.timestamp
|
|
2882
2923
|
});
|
|
2883
2924
|
}
|
|
2884
2925
|
continue;
|
|
2885
2926
|
}
|
|
2886
|
-
if (event
|
|
2927
|
+
if (isProviderEvent(event, provider, "error")) {
|
|
2887
2928
|
messages.push({
|
|
2888
|
-
id:
|
|
2929
|
+
id: `${provider}-error-${event.timestamp}`,
|
|
2889
2930
|
type: "error",
|
|
2890
|
-
message: typeof event.payload.message === "string" ? event.payload.message :
|
|
2931
|
+
message: typeof event.payload.message === "string" ? event.payload.message : `${provider} failed`,
|
|
2891
2932
|
timestamp: event.timestamp
|
|
2892
2933
|
});
|
|
2893
2934
|
continue;
|
|
2894
2935
|
}
|
|
2895
|
-
if (event
|
|
2936
|
+
if (isProviderEvent(event, provider, "turn-complete")) {
|
|
2896
2937
|
for (const message of messages) {
|
|
2897
2938
|
if (message.type === "reasoning" && message.status === "in_progress") message.status = "completed";
|
|
2898
2939
|
}
|
|
2940
|
+
activeChunks.agent_message_chunk = void 0;
|
|
2941
|
+
activeChunks.agent_thought_chunk = void 0;
|
|
2942
|
+
continue;
|
|
2943
|
+
}
|
|
2944
|
+
if (event.type === "replicas-tool-input-request" && typeof event.payload.toolUseId === "string" && typeof event.payload.requestId === "string") {
|
|
2945
|
+
const message = messages.find((candidate) => candidate.type === "tool_call" && candidate.id === `${provider}-tool-${event.payload.toolUseId}`);
|
|
2946
|
+
if (message && (Array.isArray(event.payload.options) || Array.isArray(event.payload.questions))) {
|
|
2947
|
+
const resolved = resolvedInputs.get(event.payload.toolUseId);
|
|
2948
|
+
message.inputRequest = {
|
|
2949
|
+
requestId: event.payload.requestId,
|
|
2950
|
+
status: resolved?.selectionId === "aborted" ? "aborted" : resolved ? "resolved" : "pending",
|
|
2951
|
+
...Array.isArray(event.payload.options) ? { options: event.payload.options } : {},
|
|
2952
|
+
...Array.isArray(event.payload.questions) ? { questions: event.payload.questions } : {},
|
|
2953
|
+
...resolved ? { selectionId: resolved.selectionId } : {},
|
|
2954
|
+
...resolved?.selectionSummary ? { selectionSummary: resolved.selectionSummary } : {}
|
|
2955
|
+
};
|
|
2956
|
+
}
|
|
2899
2957
|
continue;
|
|
2900
2958
|
}
|
|
2901
|
-
if (event.type
|
|
2959
|
+
if (event.type === "replicas-tool-input-resolved" && typeof event.payload.toolUseId === "string" && typeof event.payload.selectionId === "string") {
|
|
2960
|
+
const resolved = {
|
|
2961
|
+
selectionId: event.payload.selectionId,
|
|
2962
|
+
...typeof event.payload.selectionSummary === "string" ? { selectionSummary: event.payload.selectionSummary } : {}
|
|
2963
|
+
};
|
|
2964
|
+
resolvedInputs.set(event.payload.toolUseId, resolved);
|
|
2965
|
+
const message = messages.find((candidate) => candidate.type === "tool_call" && candidate.id === `${provider}-tool-${event.payload.toolUseId}`);
|
|
2966
|
+
if (message?.inputRequest) {
|
|
2967
|
+
message.inputRequest = {
|
|
2968
|
+
...message.inputRequest,
|
|
2969
|
+
status: resolved.selectionId === "aborted" ? "aborted" : "resolved",
|
|
2970
|
+
...resolved
|
|
2971
|
+
};
|
|
2972
|
+
}
|
|
2973
|
+
continue;
|
|
2974
|
+
}
|
|
2975
|
+
if (!isProviderEvent(event, provider, "session-update") || !isRecord(event.payload.update)) continue;
|
|
2902
2976
|
const update = event.payload.update;
|
|
2903
2977
|
const kind = update.sessionUpdate;
|
|
2904
2978
|
if (kind === "agent_message_chunk" || kind === "agent_thought_chunk") {
|
|
2905
|
-
const
|
|
2906
|
-
if (
|
|
2907
|
-
const
|
|
2979
|
+
const content = getAcpTextChunk(update, kind);
|
|
2980
|
+
if (content === null) continue;
|
|
2981
|
+
const explicitId = typeof update.messageId === "string" && update.messageId.length > 0 ? `${provider}-${kind}-${update.messageId}` : null;
|
|
2982
|
+
const id = explicitId ?? activeChunks[kind] ?? `${provider}-${kind}-${chunkSequence++}`;
|
|
2983
|
+
activeChunks[kind] = id;
|
|
2984
|
+
const otherKind = kind === "agent_message_chunk" ? "agent_thought_chunk" : "agent_message_chunk";
|
|
2985
|
+
if (!explicitId) activeChunks[otherKind] = void 0;
|
|
2908
2986
|
const existing = messages.find((message) => message.id === id);
|
|
2909
|
-
const text = `${existing && "content" in existing && typeof existing.content === "string" ? existing.content : ""}${
|
|
2987
|
+
const text = `${existing && "content" in existing && typeof existing.content === "string" ? existing.content : ""}${content}`.replace(provider === "fx" ? FX_CONTEXT_DIAGNOSTIC : /$^/, "");
|
|
2988
|
+
if (!text) continue;
|
|
2910
2989
|
upsertDisplayMessage(messages, kind === "agent_thought_chunk" ? { id, type: "reasoning", content: text, status: "in_progress", timestamp: existing?.timestamp ?? event.timestamp } : { id, type: "agent", content: text, timestamp: existing?.timestamp ?? event.timestamp });
|
|
2911
2990
|
continue;
|
|
2912
2991
|
}
|
|
2992
|
+
activeChunks.agent_message_chunk = void 0;
|
|
2993
|
+
activeChunks.agent_thought_chunk = void 0;
|
|
2913
2994
|
if ((kind === "tool_call" || kind === "tool_call_update") && typeof update.toolCallId === "string") {
|
|
2914
|
-
const id =
|
|
2995
|
+
const id = `${provider}-tool-${update.toolCallId}`;
|
|
2915
2996
|
const existing = messages.find((message) => message.id === id && message.type === "tool_call");
|
|
2916
2997
|
const status = update.status === "completed" || update.status === "failed" ? update.status === "failed" ? "failed" : "completed" : "in_progress";
|
|
2998
|
+
const contentOutput = Array.isArray(update.content) && update.content.length > 0 ? stringifyDisplayValue(update.content) : void 0;
|
|
2917
2999
|
upsertDisplayMessage(messages, createCallDisplayMessage({
|
|
2918
3000
|
id,
|
|
2919
|
-
server:
|
|
2920
|
-
tool: typeof update.name === "string" ? update.name : typeof update.title === "string" ? update.title : existing?.tool ?? "tool",
|
|
3001
|
+
server: provider,
|
|
3002
|
+
tool: typeof update.name === "string" && update.name ? update.name : typeof update.title === "string" && update.title ? update.title : existing?.tool ?? (typeof update.kind === "string" ? update.kind : "tool"),
|
|
2921
3003
|
input: update.rawInput === void 0 ? existing?.input : isRecord(update.rawInput) ? update.rawInput : stringifyDisplayValue(update.rawInput),
|
|
2922
|
-
output: update.rawOutput === void 0 ? existing?.output : stringifyDisplayValue(update.rawOutput),
|
|
3004
|
+
output: update.rawOutput === void 0 ? contentOutput ?? existing?.output : stringifyDisplayValue(update.rawOutput),
|
|
2923
3005
|
status,
|
|
2924
|
-
timestamp: event.timestamp
|
|
3006
|
+
timestamp: existing?.timestamp ?? event.timestamp
|
|
2925
3007
|
}));
|
|
3008
|
+
continue;
|
|
3009
|
+
}
|
|
3010
|
+
const plan = kind === "plan" ? update : isRecord(update.plan) ? update.plan : void 0;
|
|
3011
|
+
if ((kind === "plan" || kind === "plan_update") && plan && Array.isArray(plan.entries)) {
|
|
3012
|
+
const items = plan.entries.flatMap((entry) => {
|
|
3013
|
+
if (!isRecord(entry) || typeof entry.content !== "string") return [];
|
|
3014
|
+
const itemStatus = entry.status === "completed" || entry.status === "in_progress" ? entry.status : "pending";
|
|
3015
|
+
return [{
|
|
3016
|
+
text: entry.content,
|
|
3017
|
+
completed: entry.status === "completed",
|
|
3018
|
+
itemStatus
|
|
3019
|
+
}];
|
|
3020
|
+
});
|
|
3021
|
+
upsertDisplayMessage(messages, {
|
|
3022
|
+
id: `${provider}-plan-${typeof plan.planId === "string" ? plan.planId : "current"}`,
|
|
3023
|
+
type: "todo_list",
|
|
3024
|
+
items,
|
|
3025
|
+
status: items.every((item) => item.completed) ? "completed" : "in_progress",
|
|
3026
|
+
timestamp: event.timestamp
|
|
3027
|
+
});
|
|
2926
3028
|
}
|
|
2927
3029
|
}
|
|
2928
3030
|
return messages;
|
|
2929
3031
|
}
|
|
2930
3032
|
|
|
3033
|
+
// ../shared/src/display-message/parsers/fx-parser.ts
|
|
3034
|
+
function parseFxEvents(events) {
|
|
3035
|
+
return parseAcpEvents(events, "fx");
|
|
3036
|
+
}
|
|
3037
|
+
|
|
2931
3038
|
// ../shared/src/display-message/parsers/opencode-parser.ts
|
|
2932
3039
|
function timestampFromMs(value, fallback) {
|
|
2933
3040
|
return typeof value === "number" && Number.isFinite(value) ? new Date(value).toISOString() : fallback;
|
|
@@ -3190,7 +3297,11 @@ function parseClaudeEvents(events, parentToolUseId) {
|
|
|
3190
3297
|
const assistantTextCounts = /* @__PURE__ */ new Map();
|
|
3191
3298
|
const acceptedUserMessageIndexes = /* @__PURE__ */ new Set();
|
|
3192
3299
|
const canonicalUserMessageIndexes = /* @__PURE__ */ new Set();
|
|
3193
|
-
const findMatchingUserMessageIndex = (indexes, content, timestamp) => {
|
|
3300
|
+
const findMatchingUserMessageIndex = (indexes, content, timestamp, messageId) => {
|
|
3301
|
+
if (typeof messageId === "string") {
|
|
3302
|
+
const byId = messages.findIndex((message) => message.type === "user" && message.id === messageId);
|
|
3303
|
+
if (byId !== -1) return byId;
|
|
3304
|
+
}
|
|
3194
3305
|
return [...indexes].find((index) => {
|
|
3195
3306
|
const message = messages[index];
|
|
3196
3307
|
return message?.type === "user" && areUserMessagesWithinMatchWindow(message, { content, timestamp });
|
|
@@ -3208,7 +3319,7 @@ function parseClaudeEvents(events, parentToolUseId) {
|
|
|
3208
3319
|
const images = userMessageImages(event.payload.images);
|
|
3209
3320
|
if (content || images) {
|
|
3210
3321
|
const messageId = event.payload[USER_MESSAGE_ID_PAYLOAD_KEY];
|
|
3211
|
-
const canonicalIndex = findMatchingUserMessageIndex(canonicalUserMessageIndexes, content, event.timestamp);
|
|
3322
|
+
const canonicalIndex = findMatchingUserMessageIndex(canonicalUserMessageIndexes, content, event.timestamp, messageId);
|
|
3212
3323
|
const canonical = canonicalIndex === void 0 ? void 0 : messages[canonicalIndex];
|
|
3213
3324
|
const message = {
|
|
3214
3325
|
id: typeof messageId === "string" ? messageId : `user-${event.timestamp}`,
|
|
@@ -3283,14 +3394,16 @@ function parseClaudeEvents(events, parentToolUseId) {
|
|
|
3283
3394
|
};
|
|
3284
3395
|
}).filter((img) => img.data);
|
|
3285
3396
|
if (textContent || images.length > 0) {
|
|
3397
|
+
const stampedId = event.payload[USER_MESSAGE_ID_PAYLOAD_KEY];
|
|
3286
3398
|
const acceptedIndex = findMatchingUserMessageIndex(
|
|
3287
3399
|
acceptedUserMessageIndexes,
|
|
3288
3400
|
textContent,
|
|
3289
|
-
event.timestamp
|
|
3401
|
+
event.timestamp,
|
|
3402
|
+
stampedId
|
|
3290
3403
|
);
|
|
3291
3404
|
const accepted = acceptedIndex === void 0 ? void 0 : messages[acceptedIndex];
|
|
3292
3405
|
const message = {
|
|
3293
|
-
id: accepted?.id ?? `user-${event.timestamp}
|
|
3406
|
+
id: accepted?.id ?? (typeof stampedId === "string" ? stampedId : `user-${event.timestamp}`),
|
|
3294
3407
|
type: "user",
|
|
3295
3408
|
content: textContent || (images.length > 0 ? `[${images.length} image${images.length > 1 ? "s" : ""} attached]` : ""),
|
|
3296
3409
|
images: images.length > 0 ? images : accepted?.type === "user" ? accepted.images : void 0,
|
|
@@ -3680,6 +3793,9 @@ function parseAgentEvents(events, agentType) {
|
|
|
3680
3793
|
if (agentType === "fx") {
|
|
3681
3794
|
return parseFxEvents(events);
|
|
3682
3795
|
}
|
|
3796
|
+
if (agentType === "kimi") {
|
|
3797
|
+
return parseAcpEvents(events, "kimi");
|
|
3798
|
+
}
|
|
3683
3799
|
if (agentType === "opencode") {
|
|
3684
3800
|
return parseOpencodeEvents(events);
|
|
3685
3801
|
}
|
|
@@ -3791,7 +3907,7 @@ var DEFAULT_WORKSPACE_FILTERS = {
|
|
|
3791
3907
|
var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
|
|
3792
3908
|
|
|
3793
3909
|
// src/index.ts
|
|
3794
|
-
var API_URL = process.env.REPLICAS_API_URL || "https://api.
|
|
3910
|
+
var API_URL = process.env.REPLICAS_API_URL || "https://api.replicas.dev";
|
|
3795
3911
|
var API_KEY = process.env.REPLICAS_API_KEY;
|
|
3796
3912
|
if (!API_KEY) {
|
|
3797
3913
|
console.error("REPLICAS_API_KEY environment variable required");
|
|
@@ -3823,7 +3939,7 @@ function result(data, isError = false) {
|
|
|
3823
3939
|
}
|
|
3824
3940
|
var server = new McpServer({
|
|
3825
3941
|
name: "replicas-mcp",
|
|
3826
|
-
version: "0.2.
|
|
3942
|
+
version: "0.2.15"
|
|
3827
3943
|
});
|
|
3828
3944
|
var registerTool = server.registerTool.bind(server);
|
|
3829
3945
|
var codingAgentSchema = z3.string().refine(isValidCodingAgentProvider, {
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
VALID_LIFECYCLE_POLICIES,
|
|
19
19
|
} from '../../shared/src/routes/workspaces';
|
|
20
20
|
|
|
21
|
-
const API_URL = process.env.REPLICAS_API_URL || 'https://api.
|
|
21
|
+
const API_URL = process.env.REPLICAS_API_URL || 'https://api.replicas.dev';
|
|
22
22
|
const API_KEY = process.env.REPLICAS_API_KEY;
|
|
23
23
|
|
|
24
24
|
if (!API_KEY) {
|
|
@@ -71,7 +71,7 @@ type RegisterToolFn = (
|
|
|
71
71
|
|
|
72
72
|
const server = new McpServer({
|
|
73
73
|
name: 'replicas-mcp',
|
|
74
|
-
version: '0.2.
|
|
74
|
+
version: '0.2.15',
|
|
75
75
|
});
|
|
76
76
|
|
|
77
77
|
const registerTool = server.registerTool.bind(server) as RegisterToolFn;
|